/*
 *   The library for marketSim.c
 *   Includes all the functions called from marketSim.c and subsequent calls.
 *
 */


// ****************************************************************
void *Prod (void *arg){
	queue *q = (queue *) arg;
	int i;
  
	// for (i=0; i<QUEUESIZE; i++){
	while (1) {
		pthread_mutex_lock (q->mut);
		while (q->full) {
			printf ("*** Incoming Order Queue is FULL.\n"); fflush(stdout);
			pthread_cond_signal(&stuck);
			pthread_cond_wait (q->notFull, q->mut);
		}
		queueAdd (q, makeOrder());
		pthread_mutex_unlock (q->mut);
		pthread_cond_signal (q->notEmpty);
	}
}


// ****************************************************************
void *Cons (void *arg){
  
	arguments *args = (arguments *) arg;
	queue *q = (queue *) args->q;
	int i;
	order ord;
  
	//for (i=0; i<QUEUESIZE; i++){
	while (1) {
		pthread_mutex_lock (q->mut);
		while (q->empty) {
			printf ("*** Incoming Order Queue is EMPTY.\n"); fflush(stdout);
			pthread_cond_wait (q->notEmpty, q->mut);
		}


		queueDel (q, &ord);
		pthread_mutex_unlock (q->mut);
		pthread_cond_signal (q->notFull);
    

		sort_order( ord, args ); // Sort that order

	}
}

// ****************************************************************
order makeOrder(){

	static count = 0;

	int magnitude = 10; 
	int waitmsec;

	order ord;

	// wait for a random amount of time in useconds
	waitmsec = ((double)rand()/(double)RAND_MAX * magnitude);
	usleep(waitmsec*1000);
  
	ord.id = count++;
	ord.timestamp = getTimestamp();

	// Buy or Sell
	ord.action = ((double)rand()/(double)RAND_MAX <= 0.5) ? 'B' : 'S';

	// Order type
	double u2 = ((double)rand()/(double)RAND_MAX);
	if (u2 < 0.2){ 
		ord.type = 'M';                 // Market order
		ord.vol = (1 + rand()%50)*100;

	}else if (0.2 <= u2 && u2 < 0.4){
		ord.type = 'L';                 // Limit order
		ord.vol = (1 + rand()%50)*100;
		ord.price1 = currentPriceX10 + 10*(0.5 -((double)rand()/(double)RAND_MAX));
    
	}else if (0.4 <= u2 && u2 < 0.6){
		ord.type = 'S';                 // Stop order
		ord.vol = (1 + rand()%50)*100;
		ord.price1 = currentPriceX10 + 10*(0.5 -((double)rand()/(double)RAND_MAX));

	}else if (0.6 <= u2 && u2 < 0.8){
		ord.type = 'T';                 // Stop Limit order
		ord.vol = (1 + rand()%50)*100;
		ord.price1 = currentPriceX10 + 10*(0.5 -((double)rand()/(double)RAND_MAX));
		ord.price2 = currentPriceX10 + 10*(0.5 -((double)rand()/(double)RAND_MAX));

	}else if (0.8 <= u2){
		ord.type = 'C';                 // Cancel order
		ord.oldid = ((double)rand()/(double)RAND_MAX)*count;
	}

	//  dispOrder(ord);

	return (ord); 
}

// ****************************************************************
inline long getTimestamp(){

	gettimeofday(&endwtime, NULL);

	return((double)((endwtime.tv_usec - startwtime.tv_usec)/1.0e6
			+ endwtime.tv_sec - startwtime.tv_sec)*1000);
}

// ****************************************************************
void dispOrder(order ord){

	printf("%08d ", ord.id);
	printf("%08d ", ord.timestamp);  
	switch( ord.type ) {
	case 'M':
		printf("%c ", ord.action);
		printf("Market (%4d)        ", ord.vol); break;
	case 'L':
		printf("%c ", ord.action);
		printf("Limit  (%4d,%5.1f) ", ord.vol, (float) ord.price1/10.0); break;
	case 'S':
		printf("%c ", ord.action);
		printf("Stop   (%4d,%5.1f) ", ord.vol, (float) ord.price1/10.0); break;
	case 'T':
		printf("%c ", ord.action);
		printf("StopLim(%4d,%5.1f,%5.1f) ", 
		       ord.vol, (float) ord.price1/10.0, (float) ord.price2/10.0); break;
	case 'C':
		printf("* Cancel  %6d        ", ord.oldid); break;
	default : break;
	}
	printf("\n");
}

// ****************************************************************
// ****************************************************************
queue *queueInit (void)
{
	queue *q;

	q = (queue *)malloc ( sizeof (queue));
	if (q == NULL) return (NULL);

	q->empty = 1;
	q->full = 0;
	q->head = 0;
	q->tail = 0;
	q->mut = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
	pthread_mutex_init (q->mut, NULL);
	q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
	pthread_cond_init (q->notFull, NULL);
	q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
	pthread_cond_init (q->notEmpty, NULL);
  
	return (q);
}

// ****************************************************************
void queueDelete (queue *q)
{
	pthread_mutex_destroy (q->mut);
	free (q->mut);	
	pthread_cond_destroy (q->notFull);
	free (q->notFull);
	pthread_cond_destroy (q->notEmpty);
	free (q->notEmpty);
	free (q);
}

// ****************************************************************
void queueAdd (queue *q, order in)
{
	q->item[q->tail] = in;
	q->tail++;
	if (q->tail == QUEUESIZE)
		q->tail = 0;
	if (q->tail == q->head)
		q->full = 1;
	q->empty = 0;

	return;
}

// ****************************************************************
void queueDel (queue *q, order *out)
{
	*out = q->item[q->head];

	q->head++;
	if (q->head == QUEUESIZE)
		q->head = 0;
	if (q->head == q->tail)
		q->empty = 1;
	q->full = 0;

	return;
}

// ****************************************************************
/*  Sort Order is called from Consumer thread.
 *  Resbonsible for putting every order in 
 *  the right queue.
 *
 */
void sort_order( order ord , arguments *arg ){
	// Initialization necessary for almost every function
	arguments *args = (arguments *) arg;
	queue *b = args->b;
	queue *s = args->s;
	queue *bl = args->bl;
	queue *sl = args->sl;
	queue *sstop = args->sstop;
	queue *bstop = args->bstop;
	queue *blstop = args->blstop;
	queue *slstop = args->slstop;
	queue *cancel = args->cancel;


	int i;

	switch (ord.action){
	case 'B':
		switch(ord.type){
		case 'M':
			pthread_mutex_lock(b->mut);
			while (b->full){
				printf("*** Buy Order Queue is FULL.\n"); fflush(stdout);
				pthread_cond_wait(b->notFull, b->mut);
			}
			queueAdd(b,ord);
			pthread_mutex_unlock(b->mut);
			pthread_cond_signal(b->notEmpty);
			break;
		case 'L':
			pthread_mutex_lock(bl->mut);
			while (bl->full){
				printf("*** Buy Limit Order Queue is FULL.\n"); fflush(stdout);
				pthread_cond_wait(bl->notFull, bl->mut);
			}
			InsertHeapMax(bl, ord); 
			pthread_mutex_unlock(bl->mut);
			pthread_cond_signal(bl->notEmpty);
			break;      
		case 'S':
			pthread_mutex_lock(bstop->mut);
			while(bstop->full){
				printf("*** Buy Stop Order Queue is FULL.\n");
				pthread_cond_wait(bstop->notFull,bstop->mut);
			}
			InsertHeapMin(bstop,ord);
			pthread_mutex_unlock(bstop->mut);
			pthread_cond_signal(bstop->notEmpty);
			break;
		case 'T':
			pthread_mutex_lock(blstop->mut);
			while(blstop->full){
				printf("*** Buy Stop Limit Order Queue is Full.\n");
				pthread_cond_wait(blstop->notFull,blstop->mut);
			}
			InsertHeapMin(blstop,ord);
			pthread_mutex_unlock(blstop->mut);
			pthread_cond_signal(blstop->notEmpty);
			break;
		case 'C':
			pthread_mutex_lock( cancel->mut);
			while(cancel->full){
				printf("*** Cancel Order Queue is FULL.\n");
				pthread_cond_wait(cancel->notFull, cancel->mut);
			}
			queueAdd( cancel,ord);
			pthread_mutex_unlock( cancel->mut);
			pthread_cond_signal( cancel->notEmpty);
			break;
		}
		break;  
	case 'S':
		switch (ord.type){
		case 'M':
			pthread_mutex_lock(s->mut);
			while(s->full) {
				printf("*** Sell Order Queue is FULL.\n"); fflush(stdout);
				pthread_cond_wait(s->notFull, s->mut);
			}
			queueAdd(s,ord);
			pthread_mutex_unlock(s->mut);
			pthread_cond_signal(s->notEmpty);
			break;
		case 'L':
			pthread_mutex_lock(sl->mut);
			while(sl->full) {
				printf("*** Sell Limit Order Queue is FULL.\n"); fflush(stdout);
				pthread_cond_wait(sl->notFull, sl->mut);
			}
			InsertHeapMin(sl, ord);
			pthread_mutex_unlock(sl->mut);
			pthread_cond_signal(sl->notEmpty);
			break;
		case 'S':
			pthread_mutex_lock( sstop->mut);
			while(sstop->full){
				printf("*** Sell Stop Order Queue is FULL.\n");
				pthread_cond_wait(sstop->notFull,sstop->mut);
			}
			InsertHeapMax(sstop,ord);
			pthread_mutex_unlock(sstop->mut);
			pthread_cond_signal(sstop->notEmpty);
			break;
		case 'T':
			pthread_mutex_lock(slstop->mut);
			while(slstop->full){
				printf("*** Sell Stop Limit Order Queue is Full.\n");
				pthread_cond_wait(slstop->notFull,slstop->mut);
			}
			InsertHeapMin(slstop,ord);
			pthread_mutex_unlock(slstop->mut);
			pthread_cond_signal(slstop->notEmpty);
			break;
		case 'C':
			pthread_mutex_lock( cancel->mut);
			while(cancel->full){
				printf("*** Cancel Order Queue is FULL.\n");
				pthread_cond_wait(cancel->notFull, cancel->mut);
			}
			queueAdd( cancel,ord);
			pthread_mutex_unlock( cancel->mut);
			pthread_cond_signal( cancel->notEmpty);
			break;
		}
		break;
	}
}

// ****************************************************************
/*
 *  Quick Insertion in binary heap.
 *  Reference: http://cprogramminglanguage.net/binary-heap-c-code.asp
 *
 */
void InsertHeapMin(queue *q, order ord){  // Checkarismeno doulevei kala
	//  Binary Min Heap
	int i;
	/* element q->item[ 0 ] is a sentinel */
	for ( i = ++q->tail; q->item[ i / 2 ].price1 > ord.price1 ; i /= 2 ){
		q->item[ i ] = q->item[ i / 2 ];
		if ( i == 0 ) break;
	}
	q->item[i] = ord;
  
  
	if (((q->tail) + 1) == QUEUESIZE)
		q->full = 1;
	q->empty = 0;
  
}

// ****************************************************************

void InsertHeapMax( queue *q, order ord){
	int j;
	if (q->tail == 0){
		q->item[q->head]=ord;
		(q->tail)++;
	}else{
		for (j = q->tail++; q->item[ j / 2 ].price1 <= ord.price1 ; j /= 2){
			q->item[ j ] = q->item[ j / 2];                          
			if (j==0) break;
		}
		q->item[ j ] = ord;                                                    
	}
	if (((q->tail)) == QUEUESIZE-1)
		q->full = 1;
	q->empty = 0;
}

// ****************************************************************              
// Delete min from binary heap and percolate

void DeleteMin( queue *q, order *disc){
	int i,Child;
	order minord,lastord,discard;

	minord = q->item[ 1 ];
	lastord = q->item[ q->tail-- ];
	for (i = 1;  i * 2  <= q->tail; i = Child ){
		/* Find smaller child */
		Child =  i * 2;
		if (Child != q->tail && q->item[ Child + 1 ].price1 < q-> item[ Child ].price1 )
			Child++;
		/* Percolate one level */
    
		if( lastord.price1 > q->item[ Child ].price1 )
			q->item[ i ] = q->item[ Child ];
		else 
			break;
	}
	q->item[ i ] = lastord;
  
	if (q->tail == 0)
		q->empty = 1;
	q->full = 0;
  
	*disc = minord;
}
// **********************************************************************
// Delete max from binary heap and percolate
// Opposite from DeleteMin
// No sentinel here
// q->item[0] is the order with the maximum price

void DeleteMax( queue *q, order *disc){

	int i,Child;
	order maxord,lastord,discard;
  
	maxord = q->item[ 0 ];
	lastord = q->item[ --q->tail ];
	for (i = 0;  i * 2  <= q->tail-1; i = Child ){
		/* Find bigger  child */
		Child =  i * 2 + 1;
		if (Child != q->tail && q->item[ Child + 1 ].price1 > q-> item[ Child ].price1 )
			Child++;
		/* Percolate one level */
		if( lastord.price1 < q->item[ Child ].price1 )
			q->item[ i ] = q->item[ Child ];
		else 
			break;
	}
	q->item[ i ] = lastord;

	if (q->tail == 0)
		q->empty= 1;
	q->full = 0;
  
	*disc = maxord;
}
  

// ****************************************************************
// Writes in log file. Concurrency must be considered from caller.
void trace(long time, int price, int vol, order ord1, order ord2, FILE *f) 
{
  

	fprintf(f,"Time:%ld Price:%5.1f Volume:%d Order1:%ld %c %c Timestamp1: %ld Order2:%ld %c %c Timestamp2: %ld\n",time,(float) price/10.0,vol,ord1.id,ord1.action, ord1.type,ord1.timestamp, ord2.id, ord2.action, ord2.type,ord2.timestamp);
}

// ****************************************************************
/*
 *   BMSM thread ( see report )
 *
 */
void *BuyMarketSellMarket( void *arg )
{
	arguments *args = ( arguments *) arg;
	queue *b = args->b;
	queue *s = args->s;
	queue *bl = args->bl;
	queue *sl = args->sl;
	order discard;
	while(1){
		pthread_mutex_lock(b->mut);
		pthread_mutex_lock(s->mut);

		if ( !(b->empty) && !(s->empty) ){
			if( (sl->item[ 1 ].price1 > currentPriceX10 && bl->item [ 0 ].price1 < currentPriceX10) )
				Transaction( b , s );
		}
		pthread_mutex_unlock( b->mut );
		pthread_mutex_unlock( s->mut );
	}
}
// ****************************************************************
// BMSL thread ( see report )
void *BuyMarketSellLimit( void *arg )
{
	arguments *args = ( arguments *) arg;
  
	queue *b = args->b;
	queue *s = args->s;
	queue *bl = args->bl;
	queue *sl = args->sl;
	order discard;
	while(1){
		pthread_mutex_lock( b->mut );
		pthread_mutex_lock( sl->mut );

		if (!(b->empty) && !(sl->empty)){
			if ( (sl->item[ 1 ].price1 < currentPriceX10) || s->empty ){
				pthread_mutex_lock( &price );
				Transaction( b , sl );
				pthread_mutex_unlock( &price );
		
			}
		}
		pthread_mutex_unlock( b->mut);
		pthread_mutex_unlock( sl->mut );
	}
}

// ****************************************************************
// BLSM thread ( see report )
void *BuyLimitSellMarket( void *arg )
{
	arguments *args = ( arguments *) arg;
  
	queue *b = args->b;
	queue *s = args->s;
	queue *bl = args->bl;
	queue *sl = args->sl;
	order discard;
	while(1){
		pthread_mutex_lock( bl->mut );
		pthread_mutex_lock( s->mut );

		if( !(bl->empty) && !(s->empty)){
			if( (bl->item[ 0 ].price1 > currentPriceX10) || (b->empty) ){
				pthread_mutex_lock( &price );
				Transaction( bl ,s );
				pthread_mutex_unlock( &price );
			}
		}
		pthread_mutex_unlock( bl->mut );
		pthread_mutex_unlock( s->mut );
	}
} 


// ****************************************************************
// BLSL thread ( see report )
void *BuyLimitSellLimit( void *arg )
{
	arguments *args = ( arguments *) arg;
  
	queue *b = args->b;
	queue *s = args->s;
	queue *bl = args->bl;
	queue *sl = args->sl;
	order discard;
	while(1){
		pthread_mutex_lock( bl->mut );
		pthread_mutex_lock( sl->mut );

		if( !(bl->empty) && !(sl->empty) ){
			if( sl->item[ 1 ].price1 < bl->item[ 0 ].price1  ){
				pthread_mutex_lock( &price );
				Transaction( bl , sl );
				pthread_mutex_unlock( &price );
			}	
		}      
		pthread_mutex_unlock( bl->mut );
		pthread_mutex_unlock( sl->mut );
	
	}
}

// ************************************************************************
/*
 *   Function is responsible for any kind of transaction
 *   The choice is made from reading argument's type
 *   It also changes currentPriceX10 and removes orders from queues when 
 *   necessary
 */
void Transaction( queue *b, queue *s ){


	order disc,disc2;

	if (b->item[ b->head ].type == 'M' && s->item[ s->head ].type == 'M'){
		if (b->item[ b->head ].vol > s->item[ s->head ].vol){

			b->item[ b->head ].vol = b->item[ b->head ].vol - s->item[ s->head ].vol;
			queueDel( s, &disc );
			pthread_cond_signal(s->notFull);
			disc2 =  b->item[ b->head ];
      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}else if (b->item[ b->head ].vol < s->item[ s->head ].vol){

			s->item[ s->head ].vol = s->item[ s->head ].vol - b->item[ b->head ].vol;
			queueDel( b, &disc );
			pthread_cond_signal(b->notFull);
			disc2 = s->item[ s->head ];

			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}else{
			queueDel( s,&disc );
			queueDel( b,&disc2 );
      
			pthread_cond_signal(b->notFull);
			pthread_cond_signal(s->notFull);

			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
		}
	}else if(b->item[b->head].type == 'M' && s->item[1].type=='L'){
    
		if (b->item[ b->head ].vol > s->item[  1 ].vol){

			disc2 =  b->item[ b->head ];      
			b->item[ b->head ].vol = b->item[ b->head ].vol - s->item[1].vol;
			DeleteMin( s, &disc );
			pthread_cond_signal( s->notFull);
			currentPriceX10 = (disc.price1);


			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}else if (b->item[ b->head ].vol < s->item[1].vol){

			disc2 = s->item[ 1 ];
			s->item[ 1 ].vol = s->item[ 1 ].vol - b->item[ b->head ].vol;
			queueDel( b, &disc );
			pthread_cond_signal(b->notFull);
			currentPriceX10 = disc2.price1;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}else{
			DeleteMin( s,&disc );
			queueDel( b,&disc2 );
			pthread_cond_signal( b->notFull );
			pthread_cond_signal( s->notFull );
			currentPriceX10 = (disc.price1);

			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
			pthread_cond_signal( b->notFull );
			pthread_cond_signal( s->notFull );
		}
	}else if( b->item[ 0 ].type == 'L' && s->item[ 1 ].type == 'L' ){

		if ( b->item[ 0 ].vol > s->item[ 1 ].vol){
			disc2 = b->item[ b->head ];
			b->item[ 0 ].vol = b->item[ 0 ].vol - s->item[ 1 ].vol;
			DeleteMin( s, &disc );
			pthread_cond_signal( s->notFull );
			currentPriceX10 = (disc.price1 + disc2.price1)/2;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}else if (b->item[ 0 ].vol < s->item[ 1 ].vol ){
			disc2 = s->item[ 1 ];
			DeleteMax( b , &disc );
			pthread_cond_signal( b->notFull );
			currentPriceX10 = ( disc.price1 + disc2.price1 ) / 2;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
		}else {

			DeleteMin( s,&disc );
			DeleteMax( b,&disc2 );
			pthread_cond_signal( s->notFull );
			pthread_cond_signal( b->notFull );
			currentPriceX10 = ( disc.price1 + disc2.price1 ) / 2;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      

		}
	}else if ( b->item[ 0 ].type == 'L' && s->item[ s->head ].type == 'M'){

		if ( b->item[ 0 ].vol > s->item[ s->head].vol){
			disc2 = b->item[ 0 ];
			b->item[0].vol = b->item[ 0 ].vol - s->item[ s->head ].vol;
			queueDel( s , &disc );
			pthread_cond_signal( s->notFull );
			currentPriceX10 = disc2.price1;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      

		}else if (( b->item [ 0 ].vol ) < ( s->item[ s->head ].vol )){
			disc2 = s->item[ s->head ];
			s->item[ s->head ].vol = s->item[ s->head ].vol - b->item[ 0 ].vol;
			DeleteMax( b , &disc );
			pthread_cond_signal( b->notFull );
			currentPriceX10 = disc.price1;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
            
		}else{
			queueDel(s , &disc );
			DeleteMax( b, &disc2 );
			pthread_cond_signal(b->notFull) ;
			pthread_cond_signal(s->notFull);
			currentPriceX10 = disc2.price1;

      
			pthread_mutex_lock( &file );
			trace(getTimestamp(),currentPriceX10, disc.vol, disc, disc2, f);
			pthread_mutex_unlock( &file );
      
		}
    
	}
  
}

//***************************************************************************** 
// Buy Stop queue is min binary heap

void *BStop( void *arg )
{
	arguments *args = ( arguments *) arg;

	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *bstop = args->bstop;
	order discard;

	while(1){
		pthread_mutex_lock( bstop->mut);

		while( bstop->empty ){
			printf("*** Stop Order Queue is EMPTY.\n");
			pthread_cond_wait( bstop->notEmpty ,bstop->mut);
		}
		pthread_mutex_unlock(bstop->mut);

		if ( currentPriceX10 > bstop->item[ 1 ].price1 ){
			pthread_mutex_lock( bstop->mut );
			DeleteMin( bstop, &discard );
			pthread_mutex_unlock( bstop->mut );
			pthread_cond_signal( bstop->notFull );
			// change type and timestamp
			discard.type = 'M';
			discard.timestamp = getTimestamp();
			pthread_mutex_lock(b->mut );
			while(b->full){
				printf("*** Buy Order Queue is FULL.\n");
				pthread_cond_wait(b->notFull,b->mut);

			}
			queueAdd( b,discard);
			pthread_mutex_unlock(b->mut );
			pthread_cond_signal( b->notEmpty );

		}

	}
  
}

//***************************************************************************** 
// SSTOP QUEUE is max binary heap

void *SStop(void *arg)
{


	arguments *args = ( arguments *) arg;

	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *sstop = args->sstop;
	order discard;

	while(1){
		pthread_mutex_lock( sstop->mut);
		while( sstop->empty ){
			printf("*** Stop Order Queue is EMPTY.\n");
			pthread_cond_wait( sstop->notEmpty , sstop->mut);
		}
		pthread_mutex_unlock( sstop->mut );

		if ( currentPriceX10 < sstop->item[ 0 ].price1 ){
			pthread_mutex_lock( sstop->mut );
			DeleteMax( sstop, &discard );
			pthread_mutex_unlock( sstop->mut );
			pthread_cond_signal( sstop->notFull );
			discard.type = 'M';
			discard.timestamp = getTimestamp();
			pthread_mutex_lock( s->mut );
			while(s->full){
				printf("*** Sell Order Queue is Full.\n"); fflush(stdout);
				pthread_cond_wait(s->notFull,s->mut);
			}
			queueAdd( s, discard );
			pthread_mutex_unlock( s->mut );
			pthread_cond_signal( s->notEmpty);

		}
	}
}

//***************************************************************************** 
/*
 *  Dummy Cancel thread searches every queue for the requested Id and 
 *  deletes it when found
 */
void *Cancel( void *arg ){
  
	arguments *args = (arguments *) arg;

	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *sstop = args->sstop;
	queue *bstop = args->bstop;
	queue *cancel = args->cancel;
	order disc;
	long id;
	int i,j;
	int flag;
	while(1){

		pthread_mutex_lock( cancel->mut );
		while( cancel->empty){
			printf("*** Cancel Order Queue is Empty.\n");
			pthread_cond_wait(cancel->notEmpty, cancel->mut);
		}
		queueDel(cancel,&disc);
		pthread_mutex_unlock( cancel->mut );
		id = disc.oldid;
		// Search everywhere for the id to be cancelled
		if( SearchQueue( b, id ) ) 
			printf("Canceled\n");
		else if( SearchQueue( s, id ) ) 
			printf("Canceled\n");
		else if( SearchQueue(sl, id ) ) 
			printf("Canceled\n");
		else if( SearchQueue(bl,id) ) 
			printf("Canceled\n");
		else if( SearchQueue(sstop,id) ) 
			printf("Canceled\n");
		else if( SearchQueue(bstop,id) ) 
			printf("Canceled\n");
		else 
			printf("Not Found\n");

	}
}

//***************************************************************************** 

int SearchQueue( queue *q,long id){
	int i;

	for ( i = q->head; i != q->tail ; i =( i+1 )% QUEUESIZE ){
		if (q->item[ i ].id == id ){
			pthread_mutex_lock( q->mut );
			extract(q,i);    
			pthread_mutex_unlock(q->mut);
			pthread_cond_signal(q->notFull);
			return (1);
		}
	}
	return (0);

}

//***************************************************************************** 

// extract order from queue
void extract( queue *q, long i){
	int j;
	if( q->item[i].type == 'M'){
		/* -------------------------*/
		for(j = i; j< q->tail-1; j++)
			q->item[j] = q->item[j+1];
		q->tail--;
		if (q->tail == 0)
			q->empty= 1;
		q->full = 0;
      
	}else if( q->item[i].type =='L'){
		/*---------------------------*/
		if( q->item[i].action == 'B')
			ExtractHeapMax(q,i);  // Special treatment in order to cut from binary heap
		else if( q->item[ i].action =='S')
			ExtractHeapMin(q,i);  
	}else if( q->item[i].type =='S'){
		/*--------------------------*/
		if( q->item[i].action == 'B')
			ExtractHeapMin(q,i);
		else if( q->item[i].action == 'S')
			ExtractHeapMax(q,i);
	}
}

//**************************************************************************

void ExtractHeapMax(queue *q, long j){
	// Delete element j by deleting sub-heap head
	int i, Child;
	order lastord;

	lastord = q->item[ --q->tail ];
	for ( i = j; i*2 <= q->tail ; i = Child){
		/* Find bigger Child */
		Child = i * 2 + 1;
		if( Child != q->tail && q->item[ Child+1 ].price1 > q->item[ Child ].price1)
			Child++;
		/* Percolate one level */

		if( lastord.price1 < q->item[ Child ].price1)
			q->item[i] = q->item[ Child ];
		else
			break;
	}
	q->item[ i ] = lastord;
  
	if (q->tail == 0)
		q->empty= 1;
	q->full = 0;
  

}
  
//**************************************************************************

void ExtractHeapMin(queue *q,long j ){
	// Almost like ExtractHeapMax ....
	int i,Child;
	order *disc;
	order lastord;
  
	lastord = q->item[ q->tail --];

	for ( i = j ; i*2 <= q->tail; i = Child){
		/* Find smaller child */
		Child = i * 2;
		if ( Child != q->tail && q->item[ Child + 1 ].price1 < q->item[ Child ].price1 )
			Child++;
		/* Percolate one level */
		if ( lastord.price1 > q->item[Child].price1)
			q->item[ i ] = q->item[ Child ];
		else
			break;
	}
	q->item[ i ] = lastord;
  
	if (q->tail == 0)
		q->empty = 1;
	q->full = 0;

}

//**************************************************************************

//==============================================
// Function forces orders out of the full queue
// Necessary to avoid freezing ....
//==============================================

void *forceTrans(void *arg){
	int flag;
	arguments *args = ( arguments *) arg;
	queue *q = args->q;
	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *sstop = args->sstop;
	queue *bstop = args->bstop;
	queue *blstop = args->blstop;
	queue *slstop = args->slstop;
	queue *cancel = args->cancel;
	order discard;
  
	while(1){
		pthread_mutex_lock(&force_mut);
		pthread_cond_wait( &stuck, &force_mut );
		while(q->full){
			if( b->full ){
				pthread_mutex_lock(b->mut);
				queueDel(b,&discard);
				pthread_mutex_unlock(b->mut);
				pthread_cond_signal(b->notFull);
			}
			if( (bl->full)){
				pthread_mutex_lock(bl->mut);
				DeleteMax(bl,&discard);
				pthread_mutex_unlock(bl->mut);
				pthread_cond_signal(bl->notFull);
			}
			if( s->full ){
				pthread_mutex_lock(s->mut);
				queueDel(s,&discard);
				pthread_mutex_unlock(s->mut);
				pthread_cond_signal(s->notFull);
			}
			if( (sl->full)){
				pthread_mutex_lock(sl->mut);
				DeleteMin(sl,&discard);
				pthread_mutex_unlock(sl->mut);
				pthread_cond_signal(sl->notFull);
			}
			if( cancel->full){
				pthread_mutex_lock(cancel->mut);
				queueDel(cancel,&discard);
				pthread_mutex_unlock(cancel->mut);
				pthread_cond_signal(cancel->notFull);
			}
			if(bstop->full){
				pthread_mutex_lock(bstop->mut);
				DeleteMin(bstop,&discard);
				pthread_mutex_unlock(bstop->mut);
				pthread_cond_signal(bstop->notFull);
			}
			if(sstop->full){
				pthread_mutex_lock(sstop->mut);
				DeleteMax(sstop,&discard);
				pthread_mutex_unlock(sstop->mut);
				pthread_cond_signal(sstop->notFull);
			}
			if(blstop->full){
				pthread_mutex_lock(blstop->mut);
				DeleteMin(blstop,&discard);
				pthread_mutex_unlock(blstop->mut);
				pthread_cond_signal(blstop->notFull);
			}
			if(slstop->full){
				pthread_mutex_lock(slstop->mut);
				DeleteMax(slstop,&discard);
				pthread_mutex_unlock(slstop->mut);
				pthread_cond_signal(slstop->notFull);
			}
		}
		pthread_mutex_unlock(&force_mut);
	}  
}

/*================================================================*/

void *SStopLimit( void *arg )
{
  
	arguments *args = (arguments *) arg;
	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *slstop = args->slstop;
	order discard;
  
	while(1){
		pthread_mutex_lock( slstop->mut);
		while( slstop->empty){
			printf("***Sell Stop Limit Order Queue is Empty.\n");
			pthread_cond_wait( slstop->notEmpty, slstop->mut);
		}
		pthread_mutex_unlock(slstop->mut);
		if( currentPriceX10 < slstop->item[0].price2 ){
			pthread_mutex_lock(slstop->mut);
			DeleteMax( slstop,&discard);
			pthread_mutex_unlock(slstop->mut);
			pthread_cond_signal(slstop->notFull);
			discard.type = 'L';
			discard.timestamp = getTimestamp();
			pthread_mutex_lock(sl->mut);
			while(sl->full){
				printf("*** Sell Limit Order Queue is FULL.\n"); fflush(stdout);
				pthread_cond_wait(sl->notFull,sl->mut);

			}
			InsertHeapMin( sl, discard);
			pthread_mutex_unlock(sl->mut);
			pthread_cond_signal( sl->notEmpty);

		}
	}
}
void *BStopLimit( void *arg )
{
	arguments *args = ( arguments *) arg;

	queue *b = args->b;
	queue *s = args->s;
	queue *sl = args->sl;
	queue *bl = args->bl;
	queue *blstop = args->blstop;
	order discard;

	while(1){
		pthread_mutex_lock( blstop->mut);

		while( blstop->empty ){
			printf("***Buy Stop Limit Order Queue is EMPTY.\n");
			pthread_cond_wait( blstop->notEmpty ,blstop->mut);

		}
		pthread_mutex_unlock(blstop->mut);

		if ( currentPriceX10 > blstop->item[ 1 ].price1 ){
			pthread_mutex_lock( blstop->mut );
			DeleteMin( blstop, &discard );
			pthread_mutex_unlock( blstop->mut );
			pthread_cond_signal( blstop->notFull );
			discard.type = 'L';
			discard.timestamp = getTimestamp();
			pthread_mutex_lock(bl->mut );
			while( bl->full){
				printf("*** Buy Limit Order Queue is FULL.\n");
				pthread_cond_wait(bl->notFull,bl->mut);

			}
			InsertHeapMax( bl,discard);
			pthread_mutex_unlock(bl->mut );
			pthread_cond_signal( bl->notEmpty );

		}
	}
}
